package org.vt.eldercare.serial;

import java.io.IOException;
import java.io.InputStream;
import org.vt.eldercare.data.GUI;
import org.vt.eldercare.data.Node;
import org.vt.eldercare.dummy.SerialDummyAP;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

/**
 * This class handles the communication with the MSP430.
 * 
 * @author Michael
 * 
 * 
 */
public class TwoWaySerialComm {

	/**
	 * Representation of the COM port the MSP430 is connected on.
	 */
	private SerialPort serialPort;

	/**
	 * This is the graph in the DataAcquisitionGUI that needs to be updated
	 * whenever new Data comes in.
	 */
	private GUI gui;

	private List<Node> nodeList = new ArrayList<Node>();

	private boolean connected = false;

	/**
	 * Set the graph reference to "graph".
	 * 
	 * @param graph
	 */
	public void setGUI(GUI gui) {
		this.gui = gui;
	}

	public List<Node> getNodes() {
		return nodeList;
	}

	public boolean isConnected() {
		return connected;
	}

	/**
	 * Closes the Serial Port.
	 */
	public void close() {
		connected = false;
		if (serialPort != null) {
			serialPort.close();
		}
	}

	/**
	 * Attempts to open communication on "portName" (Typically COM5). Fails if
	 * the port is in use by another application, or if the port is not
	 * available.
	 * 
	 * @param portName
	 * @throws Exception
	 */
	public void connect(String portName) throws Exception {
		if (!portName.isEmpty()) {
			CommPortIdentifier portIdentifier = CommPortIdentifier
					.getPortIdentifier(portName);
			if (portIdentifier.isCurrentlyOwned()) {
				System.err.println("Error: Port is currently in use");
			} else {
				CommPort commPort = portIdentifier.open(this.getClass()
						.getName(), 2000);
				

				if (commPort instanceof SerialPort) {
					serialPort = (SerialPort) commPort;
					serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
							SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

					InputStream in = serialPort.getInputStream();
					System.err.println("Connected on " + portName);
					serialPort.addEventListener(new SerialReader(in));
					serialPort.notifyOnDataAvailable(true);
					connected = true;
				} else {
					System.err
							.println("Error: Only serial ports are handled by this example.");
				}
			}
		} else {
			System.err.println("Error: No Ports Connected");
		}
		
	}

	public void connect(SerialDummyAP tester) {
		System.out.println("Connected to Dummy AP.");
		tester.setSerialPortEventListener(new SerialReader(tester
				.getInputStream()));
		(new Thread(tester)).start();
	}

	/**
	 * Handles the input coming from the serial port. A new line character is
	 * treated as the end of a block.
	 */
	public class SerialReader implements SerialPortEventListener {
		private static final int USB_MESSAGE_LENGTH = 13;
		private InputStream in;

		/**
		 * Create a new SerialReader class using the InputStream "in".
		 * 
		 * @param in
		 */
		public SerialReader(InputStream in) {
			this.in = in;
		}

		public void serialEvent(SerialPortEvent arg0) {

								
				try {
					int data;
					String buffer = "";
					while ((data = in.read()) != -1) {
						if (data == 254 && !USBPortRunnable.getResponse()) {
							System.out.println("Echo Request sent");
							USBPortRunnable.setResponse(true);
						}
						else if (data != 254 && !USBPortRunnable.getResponse()) {
							System.out.println("No Echo Request");
							USBPortRunnable.setResponse(false);
						}
						else if (USBPortRunnable.getResponse()){
						//System.out.print((char) data);
						buffer += (char) data;
						if (buffer.length() >= USB_MESSAGE_LENGTH) {
							if (buffer.charAt(USB_MESSAGE_LENGTH - 1) == '\n') {
								SerialInterpreter interpreter = new SerialInterpreter(
										buffer);
								Node temp = new Node(interpreter.getNode());
								if (nodeList.contains(temp)) {
									for (Node n : nodeList) {
										if (n.getNodeID() == interpreter.getNode()) {
											temp = n;
										}
									}
								} else {
									nodeList.add(temp);
									gui.addNode(temp, (nodeList.size() == 1));
								}
								temp.addDataSet(interpreter.getDataPoint());
								interpreter.updateNodeStatus(temp);
								System.out.println("Node: " + temp.getName() + " "
										+ interpreter.outputAsString() + " Rate: "
										+ temp.getActualDataRate());
							} else {
								if (buffer.length() == USB_MESSAGE_LENGTH) {
									System.err
											.println("ERROR: Messages became offset somehow.");
								}
							}
							if (data == '\n') {
								buffer = "";
							}
						}
					}
				}} catch (IOException e) {
					e.printStackTrace();
				}
			 
		}

	
		}
	

	/**
	 * Send a command over serial. The format of the message is as follows: <br>
	 * <br>
	 * 
	 * messageBytes[0] = ID of node to send command to<br>
	 * messageBytes[1] = <b>command</b> <br>
	 * <br>
	 * 
	 * The <b>command</b> can be any of the following from the SerialWriter
	 * class: <br>
	 * <br>
	 * 
	 * (<b>SAMPLE_RATE(1 - 30)</b>): Set the sample rate to 1 - 30 samples per
	 * second <br>
	 * (<b>DISABLE_GYROSCOPE</b>): Disable the Gyroscope <br>
	 * (<b>ENABLE_GYROSCOPE</b>): Enable the Gyroscope
	 * 
	 * @param nodeID
	 *            - The ID of the node to send the command to.
	 * @param command
	 *            - The command to be sent.
	 * 
	 * @see org.vt.eldercare.serial.TwoWaySerialComm.SerialWriter
	 */
	public void serialWrite(final int nodeID, final byte command) {
		SerialWriter.serialWrite(this, nodeID, command);
	}
	
	public void serialWrite(final int nodeID, final byte[] commands){
		SerialWriter.serialWrite(this, nodeID, commands);
	}

	protected OutputStream getOutputStream() throws IOException {
		return serialPort.getOutputStream();
	}

	/**
	 * 
	 * @return A list of all connected Serial Ports
	 */
	@SuppressWarnings("unchecked")
	public static List<String> listPorts() {
		//String portName ="";
		List<String> portName = new ArrayList<String>();
		java.util.Enumeration<CommPortIdentifier> portEnum = CommPortIdentifier
				.getPortIdentifiers();
		// System.err.println("Available Ports:");
		if (!portEnum.hasMoreElements()) {
			// System.err.println("None");
		}
		while (portEnum.hasMoreElements()) {
			CommPortIdentifier portIdentifier = portEnum.nextElement();
			portName.add(portIdentifier.getName());
			//portName = portIdentifier.getName();
			// System.err.println(portIdentifier.getName() + " - "
			// + getPortTypeName(portIdentifier.getPortType()));
		}
		//return portName;
		return portName;
	}
	public static boolean checkUnplugged(List<String> portName,String connected) {

		for (String s : portName) {
			if(s==connected)
				return false;
		}
		return true;
	
	}
	@SuppressWarnings("unused")
	private static String getPortTypeName(int portType) {
		switch (portType) {
		case CommPortIdentifier.PORT_I2C:
			return "I2C";
		case CommPortIdentifier.PORT_PARALLEL:
			return "Parallel";
		case CommPortIdentifier.PORT_RAW:
			return "Raw";
		case CommPortIdentifier.PORT_RS485:
			return "RS485";
		case CommPortIdentifier.PORT_SERIAL:
			return "Serial";
		default:
			return "unknown type";
		}
	}
}
